import random
import re
from datetime import datetime

from flask import abort, jsonify
from flask import current_app
from flask import make_response
from flask import redirect
from flask import request
from flask import session

from info import constants, db
from info import redis_store
from info.libs.yuntongxun.sms import CCP
from info.models import User
from info.utils.captcha.captcha import captcha
from info.utils.response_code import RET
from . import passport_blu


@passport_blu.route('/image_code')
def get_image_code():
    """
    发送图片验证码视图函数
    :return: 图片验证码
    """

    # 1.取出参数,参数在请求中.request中可以取出参数.这个参数用来当redis数据库中的key
    # args 可以取出url ? 后面的参数
    code_id = request.args.get('code_id')

    # 2.判断是否有参数
    if not code_id:
        abort(403)

    # 3.获得生成验证码图片
    name, text, image = captcha.generate_captcha()  # name:随机生成的图片名,text:生成的验证码内容,image:生成的验证码图片
    print('图片验证码为:%s' % text)
    try:
        # 4.将验证码存储到redis数据库中并设置有效时间
        redis_store.set('Img_' + code_id, text, ex=constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)

    # 5.生成响应内容,make_response:生成响应对象方法,参数为响应内容
    resp = make_response(image)
    # 改变响应体中Content-Type的类型,上面这种形式直接返回的话时一个 html文件
    resp.headers['Content-Type'] = 'image/jpg'

    # 返回验证码图片
    return resp


@passport_blu.route('/sms_code', methods=['POST'])
def send_sms():
    """
    发送短信验证码视图函数
    :return: 发送手机验证码过程中的状态,与过期时间
    """

    # 1.接受参数,前段点击手机验证码后发送过来的数据
    args_data = request.json  # request.json 相当于 json.loads(request.data) 返回值为一个字典
    mobile = args_data['mobile']  # 手机号
    image_code = args_data['image_code']  # 用户输入的图片验证码内容
    image_code_id = args_data['image_code_id']  # 真实的图片验证码id key

    # 判断参数是否为空
    if not all([mobile, image_code, image_code_id]):
        return jsonify(errno=RET.DATAERR, errmsg='参数不全错误')

    # 判断手机号是否符合规范
    if not re.match(r'1[35678]\d{9}', mobile):
        return jsonify(errno=RET.DATAERR, errmsg='手机号格式错误')

    try:
        # redis数据库中取出真实图片验证码
        real_image_code = redis_store.get('Img_' + image_code_id)
        # 取出数据后在redis数据库中删除数据,防止再次使用次验证码通过
        redis_store.delete('Img_' + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库查询错误')

    # 判断redis数据库中取出的是否有值
    if not real_image_code:
        return jsonify(errno=RET.NODATA, errmsg='验证码已过期')

    # 判断用户输入的验证码是否正确,获取的用户验证信息为bytes类型需要转换并忽略大小写
    if image_code.encode().lower() != real_image_code.lower():
        return jsonify(errno=RET.DATAERR, errmsg='验证码输出错误')

    # 随机生成手机验证码
    sms_code = '%06d' % random.randint(0, 999999)
    print('短讯验证码为:%s' % sms_code)
    try:
        # 将手机验证码存储到redis 数据库中
        redis_store.set('sms_' + mobile, sms_code, ex=constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='手机验证码保存错误')

    # 使用云通讯发送验证码
    result = CCP().send_template_sms(mobile, [sms_code, 60], 1)  # 云通讯模板中的类 发送消息的方法,有返回值
    if result != 0:  # 短信发送成功返回 0  失败返回 1
        return jsonify(errno=RET.THIRDERR, errmsg='短信发送失败')

    # 以上流程全部通过即为手机验证码发送成功
    return jsonify(errno=RET.OK, errmsg='短信发送成功')


@passport_blu.route('/register', methods=['POST'])
def register():
    """
    注册视图函数
    :return: 注册状态
    """

    # 获得参数
    json_dict = request.json
    mobile = json_dict['mobile']  # 手机号
    smscode = json_dict['smscode']  # 短信验证码
    password = json_dict['password']  # 密码

    # 判断参数是否有值
    if not all([mobile, smscode, password]):
        return jsonify(errno=RET.NODATA, errmsg='参数错误')

    try:
        # redis数据库中取出验证码
        real_sms_code = redis_store.get('sms_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='手机验证码读取失败错误')

    # 判断redis数据库中取出的是否有值
    if not real_sms_code:
        return jsonify(errno=RET.NODATA, errmsg='验证码已过期')
    # 判断验证码
    if smscode != real_sms_code.decode():
        return jsonify(errno=RET.DATAERR, errmsg='验证码输出错误')

    # 验证成功后 redis 中删除验证码
    try:
        redis_store.delete('sms_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='删除验证码失败错误')

    # 初始化 user,设置数据
    user = User()
    user.nick_name = mobile
    user.mobile = mobile
    # 密码,需要加密
    user.password = password
    # 最后一次登陆属性, datetime.now()获得当前时间的方法
    user.last_login = datetime.now()

    try:
        # 将数据保存到mysql数据库中
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DATAERR, errmsg='数据保存错误')

    # 保存用户登陆信息, 信息保存在session中
    session['user_id'] = user.id
    session['nick_name'] = user.nick_name
    session['mobile'] = mobile

    return jsonify(errno=RET.OK, errmsg="注册成功")


@passport_blu.route('/login', methods=["POST"])
def login():
    """
    登陆视图函数
    :return: 登陆结果
    """

    # 获取参数
    json_data = request.json
    mobile = json_data['mobile']
    password = json_data['password']

    # 判断参数是否都有值
    if not all([mobile, password]):
        return jsonify(errno=RET.NODATA, errmsg='参数错误')

    try:
        # mysql数据库中查询用户,看是否注册
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='数据库取值错误')

    # 判断用户是否注册
    if not user:
        return jsonify(errno=RET.USERERR, errmsg='用户未注册')

    # 对比密码是否正确
    if not user.check_passowrd(password):
        return jsonify(errno=RET.PWDERR, errmsg="密码错误")

    # 保存登陆状态
    session['user_id'] = user.id
    session['nick_name'] = user.nick_name
    session['mobile'] = mobile

    # 最后一次登陆时间,修改数据信息数据
    user.last_login = datetime.now()
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()

    # 登陆成功
    return jsonify(errno=RET.OK, errmsg="登陆成功")


@passport_blu.route('/logout', methods=['GET', 'POST'])
def logout():
    """
    退出视图函数
    :return:
    """

    # 判断登陆是在session中查询用户信息,把session中的信息删除就可以退出了
    session.pop('user_id', None)
    session.pop('nick_name', None)  # 这种写发 第一个参数不存在是返回 None 不会报错
    session.pop('mobile', None)

    if session.get('is_admin'):
        session.pop('is_admin', None)  # 管理员退出时 清除数据
        return redirect('/')

    return jsonify(errno=RET.OK, errmsg="退出成功")